package com.googlecode.sere.core

import com.googlecode.sere.matcher._
import com.googlecode.sere.util.Logging
import com.googlecode.sere.translation._
import com.googlecode.sere.http._
import com.googlecode.sere.method.{PostResponse, ResponseEntity}

abstract class ResourceType extends Logging {
  
  type ValueType <: AnyRef

  /** URLs where this resource type can be GETed, PUT or DELETEd */
  val urls: Seq[String] = Seq()

  /** URLs where this handles post "actions" */
  val postActions: Map[String, (ResourceType, ResourceType, ResourceType.PostAction)] = Map()

  def register() = {
    urls foreach { UrlRegistry.register(_, this) }
    for ((url, data) <- postActions) { PostUrlRegistry.register(url, data) }
  }

  /*
   * We are throwing "Not Found" instead of "Method not Allowed", to avoid having to
   * send the allowed methods, which is very difficult to do statically
   */
  
  def put(k: Map[String, String], v: => ValueType, meta: InvocationData) {
    throw new NotFound
  }

  def get(k: Map[String, String], meta: InvocationData): ResponseEntity[ValueType] = {
    throw new NotFound
  }

	def delete(k: Map[String, String], meta: InvocationData) {
    throw new NotFound
  }

  def conditionalGet(k: Map[String, String], etag: String, meta: InvocationData):
    Option[ResponseEntity[ValueType]] = Some(get(k, meta))

  def parse(entity: EnclosedEntity): ValueType = {
    logger.debug("Trying to parse request of type {}", entity.mediaType)
    val (theMatch, alternatives) = ParserRegistry.get(this, entity.mediaType)
		val parsed = theMatch match {
      case Some(parser) => {
        logger.debug("Parsing request as {} with {}", entity.mediaType, parser.getClass)
        parser.parse(entity.body)
      }
      case None => throw new UnsupportedMediaType(alternatives)
		}
    parsed.asInstanceOf[ValueType]
	}
    
  def serialize(acceptedTypes: AcceptedTypes, genericObj: AnyRef): String = {

    // TODO: Is this necessary?
    val obj = genericObj.asInstanceOf[ValueType]

    logger.debug("Trying to serialize response of type {} as {}", obj.getClass, acceptedTypes)
    val (theMatch, alternatives) = SerializerRegistry.get(this, acceptedTypes)
    theMatch match {
      case Some((theType, ser)) => {
        logger.debug("Serializing response as {} with {}", theType, ser.getClass)
        ser.serialize(obj)
      }
      case None => throw new NotAcceptable(alternatives.toString)
    }
	}
}

object ResourceType {
  type PostAction = (=> AnyRef) => PostResponse
}

